# -*- coding: utf-8 -*-
#   两张表简易编辑，增删，改，查
# @Time    ： 2025/11/12  18:51
# @Auther  ： 徐良
# File     :  twoTableEdit.py

from typing import Dict, Any, Optional, Tuple
from PySide6.QtCore import Slot, QItemSelection, QModelIndex
from PySide6.QtSql import QSqlRecord, QSqlField
from baseClass.tools.panelFunction import set_label_with_date_and_count
from baseClass.model.editableQueryModel import EditableSqlModel
from baseClass.business.saveBill import SaveTableAction, SavePCTableAction
from systemSet.ui.twoPanelUI import Ui_Form
from utils.alone.aloneWorkAssistant import AloneWorkAssistant
from utils.commonBillClass import CommonBillClass
from PySide6.QtWidgets import QApplication
from baseClass.tools.dialogUtils import DialogUtils, StandardButton
from baseClass.winPanel.baseQWidget import BaseQWidget
from baseClass.delegate.defineDelegate import DefineFeldDelegate
from baseClass.winPanel.interface_style import apply_styles
from domainAndEnum.domainFunction import combox_TypeList, combox_AlignList, combox_WidgetGridList, domain_list
import ast


class TwoTableEdit(BaseQWidget, AloneWorkAssistant, CommonBillClass, Ui_Form):

    def __init__(self, p_dict:Dict[str, Any]=None,parent=None):
        super().__init__(parent)
        self.set_splitter_ratio(self.splitter, 1, 1)

        # region 初始化数据 变量
        self.m_field_list = []
        self.m_type_list = []
        self.m_edit_list = []
        self.m_visible_list = []
        self.m_join_list = []
        self.m_width_list = []
        self.m_align_list = []
        self.m_widget_list = []
        self.m_domain_list = []

        # 子表
        self.c_field_list = []
        self.c_type_list = []
        self.c_edit_list = []
        self.c_visible_list = []
        self.c_join_list = []
        self.c_width_list = []
        self.c_align_list = []
        self.c_widget_list = []
        self.c_domain_list = []

        self.master_update_set = set()
        self.master_del_set = set()
        self.master_dirty = False

        self.child_update_set = set()
        self.child_del_set = set()
        self.child_dirty = False

        # endregion

        self._master_is_data_changed_connected = False
        self._child_is_data_changed_connected = False

        self.DEFAULT_MS_DICT = p_dict
        self.ms_number = self.DEFAULT_MS_DICT.get('ms_number')
        # 创建工作类并初始化
        self.create_factory(self.DEFAULT_MS_DICT)
        # # 创建单表工作类并初始化
        self.create_table_work_class(self.DEFAULT_MS_DICT)
        self._init_map()
        self._init_config()
        self._read_master_data()
        self._read_child_data()

        self.checkBox_master.stateChanged.connect(self.on_check_box_state_master_changed)
        self.checkBox_child.stateChanged.connect(self.on_check_box_state_child_changed)

        apply_styles(self)

    def _init_map(self) -> None:
        """
        关系映射
        :return:
        """
        self.table_name = {
            '81.02.01': ('t_tableList', 't_tableListChild',),
            '81.02.02': ('t_tableForm', 't_tableFormChild',),        }

        # 必须要，配置在数据，不如在此配
        # （字段类型，列编辑，列可见，列joinSql, 列宽，列对齐，
        self.master_field_map = {
            'FId': ('Integer', 0, 0, 0, 75, 'left', 'BodyLineEditInt', None,),
            'FXh': ('Integer', 1, 1, 1, 75, 'left', 'BodyLineEditInt', None,),
            'FNumber': ('String', 1, 1, 1, 180, 'left', 'BodyLineEditStr', None,),
            'FName': ('String', 1, 1, 1, 150, 'left', 'BodyLineEditStr', None,),
            'FTableName': ('String', 1, 1, 1, 180, 'left' ,'BodyLineEditStr', None,),
            'FDescription': ('String', 1, 1, 1, 220, 'left', 'TextEditDialog', None,),
            'FSql': ('String', 1, 1, 1, 200, 'left', 'TextEditDialog', None,),
            'FWindowTitle': ('String', 1, 1, 1, 200, 'left', 'BodyLineEditStr', None,),
            'FLabelTitle': ('String', 1, 1, 1, 200, 'left', 'BodyLineEditStr', None,),
            'FDateName': ('String', 1, 1, 1, 200, 'left', 'BodyLineEditStr', None,),
            'FStatusFieldName': ('String', 1, 0, 1, 150, 'left', 'BodyLineEditStr', None,),
            'FColorGroup': ('String', 1, 1, 1, 150, 'left', 'BodyLineEditStr', None,),
            'FEnumClass': ('String', 1, 1, 1, 150, 'left', 'BodyLineEditStr', None,),
            'FSelField': ('String', 1, 1, 1, 150, 'left', 'BodyLineEditStr', None,),
            'FSelValue': ('String', 1, 1, 1, 150, 'left', 'BodyLineEditStr', None,),
            'FPrintRptNumber': ('String', 1, 1, 1, 100, 'left', 'BodyLineEditStr', None,),
        }


        # （字段类型，列编辑，列可见，列joinSql, 列宽，列对齐，控件，数值域
        self.child_field_map = {
            'FId': ('Integer', 0, 0, 0, 75, 'left','BodyLineEditInt', None),
            'FEntityId': ('Integer', 0, 1, 1, 120, 'left','BodyLineEditInt', None),
            'FXh': ('Integer', 1, 1, 1, 75, 'left','BodyLineEditInt', None),
            'FField': ('String', 1, 1, 1, 150, 'left','BodyLineEditStr', None),
            'FFieldName': ('String', 1, 1, 1, 150, 'left','BodyLineEditStr', None),
            'FFieldType': ('String', 1, 1, 1, 130, 'left','ComboxFree', combox_TypeList),
            'FDomain': ('String', 1, 1, 1, 130, 'left', 'ComboxFree', domain_list),
            'FWidget': ('String', 1, 1, 1, 130, 'left', 'ComboxFree', combox_WidgetGridList),
            'FAlign': ('String', 1, 1, 1, 100, 'left', 'ComboxFree', combox_AlignList),
            'FTableWidth': ('String', 1, 1, 1, 130, 'left','BodyLineEditInt', None),
            'FTreeWidth': ('String', 1, 1, 1, 130, 'left','BodyLineEditInt', None),
            'FIsEdit': ('String', 1, 1, 1, 100, 'left','check', None),
            'FIsVisible': ('String', 1, 0, 1, 140, 'left','check', None),
            'FJoinSql': ('String', 1, 1, 1, 120, 'left','check', None),
            'FIsF7': ('Integer', 1, 1, 1, 120, 'left','check', None),
            'FF7Str': ('String', 1, 1, 1, 120, 'left','BodyLineEditStr', None),
        }


    def _init_config(self) -> None:
        master_table, _ = self.table_name.get(self.ms_number)
        if master_table == 't_tableList':

            for field, config in self.master_field_map.items():
                field_config = {
                    'name': field,
                    'type': config[0],
                    'edit': config[1],
                    'visible': config[2],
                    'join_sql': config[3],
                    'width': config[4],
                    'align': config[5]
                }
                self.m_field_list.append(field)
                self.m_type_list.append(config[0])
                self.m_edit_list.append(config[1])
                self.m_visible_list.append(config[2])
                self.m_join_list.append(config[3])
                self.m_width_list.append(config[4])
                self.m_align_list.append(config[5])
                self.m_widget_list.append(config[6])
                self.m_domain_list.append(config[7])


            for field, config in self.child_field_map.items():
                field_config = {
                    'name': field,
                    'type': config[0],
                    'edit': config[1],
                    'visible': config[2],
                    'join_sql': config[3],
                    'width': config[4],
                    'align': config[5]
                }

                self.c_field_list.append(field)
                self.c_type_list.append(config[0])
                self.c_edit_list.append(config[1])
                self.c_visible_list.append(config[2])
                self.c_join_list.append(config[3])
                self.c_width_list.append(config[4])
                self.c_align_list.append(config[5])
                self.c_widget_list.append(config[6])
                self.c_domain_list.append(config[7])

        else:
            self.m_field_list = self.get_field_list('headSql')
            self.m_type_list =  self.get_field_type_list('headSql')
            self.m_edit_list = self.get_fields_edit_list('headSql')
            self.m_visible_list = self.get_fields_visible_list('headSql')
            self.m_join_list = self.get_join_sql_list('headSql')
            self.m_width_list = self.get_column_width_list('headSql')
            self.m_align_list = self.get_align_list('headSql')
            self.m_widget_list = self.get_field_widget_list('headSql')
            self.m_domain_list = self.get_field_domain_list('headSql')

            # -----------------------------------------------------
            # 表体
            # -----------------------------------------------------
            self.c_field_list = self.get_field_list('bodySql')
            self.c_type_list =  self.get_field_type_list('bodySql')
            self.c_edit_list = self.get_fields_edit_list('bodySql')
            self.c_visible_list = self.get_fields_visible_list('bodySql')
            self.c_join_list = self.get_join_sql_list('bodySql')
            self.c_width_list = self.get_column_width_list('bodySql')
            self.c_align_list = self.get_align_list('bodySql')
            self.c_widget_list = self.get_field_widget_list('bodySql')
            self.c_domain_list = self.get_field_domain_list('bodySql')


    # region  工具栏命令，过滤返回， 行选择变化，表单双击
    def action_select(self, q):
        sel = q.text()
        if  sel == '导出':
            # obj_list = [('生产任务单', self.taskTableView),
            #             ('生产锅单', self.potTableView),
            #             ('领料单', self.pickTableView)]
            # to_excel = TableViewToXls(obj_list)
            # to_excel.to_excel_file()
            pass

        elif sel == '保存':
            self._save_table()

        elif sel == '放弃':
            self._cancel_model_change()


    @Slot(QModelIndex)
    def on_tableview_double_clicked(self, index):
        """响应表格双击"""
        pass
        # if index.isValid():
        #     row = index.row()
        #     sender = self.sender()  # 获取发送信号的对象
        #     model = sender.model()
        #     fid = model.record(row).value('FId')
        # else:
        #     return
        #
        # ms_dict = self.DEFAULT_MS_DICT
        # if sender.objectName() == 'taskTableView':
        #     ms_dict = {'msFNumber': '04.21', 'msClass': '10', 'msSaveType': 1, 'FPID': -1}
        #
        # elif sender.objectName() == 'potTableView':
        #     ms_dict = {'msFNumber': '04.01', 'msClass': '11', 'msSaveType': 1, 'FPID': -1}
        #
        # if sender.objectName() != 'pickTableView':
        #
        #     self.create_factory(ms_dict)
        #     self.create_table_work_class(ms_dict)
        #     self.work_assistant_re_init()
        #
        # self.read_bill_data_for_edit(fid)

    @Slot(QItemSelection, QItemSelection)
    def on_selection_changed(self, selected, deselected) -> None:
        """

        :param selected:
        :param deselected:
        :return:
        """
        master_table, child_table = self.table_name.get(self.ms_number)
        indexes = selected.indexes()
        if indexes:
            row = indexes[0].row()
            fid = self.masterModel.record(row).value('FId')
            if fid is None:
                fid = -1
            self._read_child_data(fid)

    # endregion


    # region 页面数据
    def _read_master_data(self, sql: str=None, sql_name: str=None) -> None:
        """

        :return:
        """
        master_table, child_table = self.table_name.get(self.ms_number)
        sql = f"select * from {master_table} order by FNumber"

        # 先断开旧模型连接（若存在）
        if hasattr(self, 'masterModel') and self.masterModel is not None:
            if self._master_is_data_changed_connected:
                self.masterModel.dataChanged.disconnect(self.on_master_table_data_change)
                self._master_is_data_changed_connected = False  # 重置标志位

        if master_table == 't_tableList':
            sql_name = ''
        else:
            sql_name = 'headSql'

        self.masterModel = self.get_edit_model_by_sql(sql, sql_name)
        self.masterTableView.setModel(self.masterModel)

        self.masterTableView.selectionModel().selectionChanged.connect(self.on_selection_changed)

        # 仅当未连接时，建立新连接
        if not self._master_is_data_changed_connected:
            self.masterModel.dataChanged.connect(self.on_master_table_data_change)
            self._master_is_data_changed_connected = True  # 更新标志位

        p_dict = {'widget': self.m_widget_list, 'domain': self.m_domain_list}
        delegate = DefineFeldDelegate(p_dict)
        self.masterTableView.setItemDelegate(delegate)

        self.masterTableView.set_column_width_by_number(self.m_width_list)


        set_label_with_date_and_count(self.lbl_m_info, self.masterModel, 0)

    def _read_child_data(self, f_id: int=-1):
        """

        :param f_id:
        :return:
        """
        master_table, child_table = self.table_name.get(self.ms_number)
        sql = f"select * from {child_table} where FEntityId={f_id} order by FXh"
        # 先断开旧模型连接（若存在）
        if hasattr(self, 'childModel') and self.childModel is not None:
            if self._child_is_data_changed_connected:
                self.childModel.dataChanged.disconnect(self.on_child_table_data_change)
                self._child_is_data_changed_connected = False  # 重置标志位

        self.childModel = self.get_edit_model_by_sql(sql, '')
        self.childTableView.setModel(self.childModel)

        self.childTableView.set_column_width_by_number(self.c_width_list)
        # 仅当未连接时，建立新连接
        if not self._child_is_data_changed_connected:
            self.childModel.dataChanged.connect(self.on_child_table_data_change)
            self._child_is_data_changed_connected = True  # 更新标志位

        p_dict = {'widget': self.c_widget_list, 'domain': self.c_domain_list}
        delegate = DefineFeldDelegate(p_dict)
        self.childTableView.setItemDelegate(delegate)

        set_label_with_date_and_count(self.lbl_c_info, self.childModel, 0)

    def on_check_box_state_master_changed(self, state: int) -> None:
        if state == 2:
            self.masterTableView.setSortingEnabled(True)      # 开启排序
        else:
            self.masterTableView.setSortingEnabled(False)     # 关闭排序

    def on_check_box_state_child_changed(self, state: int) -> None:
        if state == 2:
            self.childTableView.setSortingEnabled(True)      # 开启排序
        else:
            self.childTableView.setSortingEnabled(False)     # 关闭排序
    # endregion


    # region 页面动作命令
    def get_selected_master_fid(self) -> Tuple[Optional[Any], int]:
        """获取主表选中行的 FID 值（无选中行返回 None）"""
        selected_rows = self.masterTableView.selectionModel().selectedRows()
        if not selected_rows:
            return None, -1

        return self.masterModel.record(selected_rows[0].row()).value('FId'), selected_rows[0].row()

    def get_selected_child_row(self) -> Optional[int]:
        """获取子表选中行的源模型索引（无选中行返回 None）"""
        selected_rows = self.childTableView.selectionModel().selectedRows()
        if not selected_rows:
            return None
        # 转换为子表源模型的行索引
        index = selected_rows[0]
        return index.row() if index.isValid() else None

    def refresh_child_fxh(self):
        """重新编号子表的 FXh 字段（从 1 开始连续）"""
        i_col = self.childModel.headers.index("FXh")
        # 按源模型行顺序重新编号 FXh
        for row in range(0, self.childModel.rowCount()):
            index = self.childModel.index(row, i_col)
            self.childModel.setData(index, row + 1)

    @Slot()
    def on_btn_m_add_clicked(self):
        """
        主表增加记录
        :return:
        """
        empty_record = QSqlRecord()
        for header in self.masterModel.headers:
            empty_record.append(QSqlField(header))

        self.masterModel.appendRecord(empty_record)
        # 滚动到新增行
        last_row = self.masterModel.rowCount() - 1
        self.masterTableView.selectRow(last_row)

    @Slot()
    def on_btn_m_ins_clicked(self):
        """
        主表插入记录
        :return:
        """
        selected_rows = self.masterTableView.selectionModel().selectedRows()
        if not selected_rows:
            DialogUtils.warn("请先选中主表中的一条记录", "警告！")
            return

        insert_row = selected_rows[0].row()
        empty_record = QSqlRecord()
        for header in self.masterModel.headers:
            empty_record.append(QSqlField(header))

        # 在 insert_row 位置插入
        self.masterModel.beginInsertRows(QModelIndex(), insert_row, insert_row)
        self.masterModel.data_rows.insert(insert_row, [None] * len(self.masterModel.headers))
        self.masterModel.endInsertRows()
        #
        # 选中新增行
        self.masterTableView.selectRow(insert_row)

    @Slot()
    def on_btn_m_del_clicked(self):
        """
        主表删除记录
        :return:
        """
        fid, row = self.get_selected_master_fid()

        if row == -1:
            DialogUtils.info("请先选中主表中的一条记录！")
            return

        if fid is None:
            self.masterModel.removeRecord(row)
            return

        # 确认删除
        reply = DialogUtils.question(f"是否删除主表 FID={fid} 的记录及关联子表记录？","确认删除")

        if reply == StandardButton.Cancel:
            return


        # 1. 删除子表中所有 FEntityId = fid 的记录
        child_rows_to_delete = []
        for row in range(self.childModel.rowCount()):
            entity_id = self.childModel.record(row).value('FEntityId')
            if entity_id == fid:
                child_rows_to_delete.append(row)
        # # 倒序删除（避免索引错乱）
        for row in reversed(child_rows_to_delete):
            child_fid = self.childModel.record(row).value('FID')
            if child_fid:
                self.child_del_set.add(child_fid)

            self.childModel.removeRecord(row)

        # 2. 删除主表选中行
        selected_rows = self.masterTableView.selectionModel().selectedRows()
        if selected_rows:
            selected_row = selected_rows[0].row()
            self.masterModel.removeRecord(selected_row)

        self.master_del_set.add(fid)
        self._set_save_button_enabled(True)

        # # 3. 更新子表过滤（如果还有主表选中行）
        # self.filter_child_by_parent()

    @Slot()
    def on_btn_c_add_clicked(self):
        """子表：增加一条记录（尾部），自动填充 FEntityId 和 FXh"""
        parent_fid, row = self.get_selected_master_fid()
        if parent_fid is None:
            if row == -1:
                info = '请先选中主表中的一条记录！'
            else:
                info = '请保存主表新增记录记录！'
            DialogUtils.warn(info, "警告")
            return

        # 创建子表记录，填充 FEntityId
        child_record = QSqlRecord()
        for header in self.childModel.headers:
            field = QSqlField(header)
            if header.lower() == "fentityid":
                field.setValue(parent_fid)
            elif header.lower() == "fxh":
                field.setValue(None)  # 后续统一编号
            else:
                field.setValue(None)
            child_record.append(field)

        # 新增记录
        self.childModel.appendRecord(child_record)

        # 重新编号 FXh
        self.refresh_child_fxh()

        # 选中新增行
        # last_proxy_row = self.child_proxy.rowCount() - 1
        # self.child_view.selectRow(last_proxy_row)

    @Slot()
    def on_btn_c_ins_clicked(self):
        """
        子表插入记录
        :return:
        """
        """子表：在当前选中行之前插入一条记录，自动填充 FEntityId 和 FXh"""
        parent_fid, row = self.get_selected_master_fid()
        child_row = self.get_selected_child_row()
        if parent_fid is None:
            if row == -1:
                info = '请先选中主表中的一条记录！'
            else:
                info = '请保存主表新增记录记录！'
            DialogUtils.info(info, "警告",)
            return
        if child_row is None:
            DialogUtils.info("请先选中子表中的一条记录！", "警告")
            return

        # 创建子表记录，填充 FEntityId
        child_record = QSqlRecord()
        for header in self.childModel.headers:
            field = QSqlField(header)
            if header.lower() == "fentityid":
                field.setValue(parent_fid)
            elif header == "FXh":
                field.setValue(None)  # 后续统一编号
            else:
                field.setValue(None)
            child_record.append(field)

        # 在选中行之前插入
        self.childModel.beginInsertRows(QModelIndex(), child_row, child_row)
        self.childModel.data_rows.insert(child_row, [child_record.value(i)
                                                      for i in range(min(child_record.count(),
                                                                         len(self.childModel.headers)))] )
        self.childModel.endInsertRows()
        # 重新编号 FXh
        self.refresh_child_fxh()

    @Slot()
    def on_btn_c_del_clicked(self):
        """
        子表删除记录
        :return:
        """
        """子表：删除选中行，重新编号 FXh"""
        child_row = self.get_selected_child_row()
        if child_row is None:
            DialogUtils.info( "请先选中子表中的一条记录！", "警告")
            return

        # 删除选中行
        fid = self.childModel.record(child_row).value('FId')
        if fid:
            self.child_del_set.add(fid)
            self._set_save_button_enabled(True)
        self.childModel.removeRecord(child_row)
        # 重新编号 FXh
        self.refresh_child_fxh()

    @Slot()
    def on_btn_c_sort_clicked(self):
        """
        子表记录排序
        :return:
        """
        self.refresh_child_fxh()

    def _cancel_model_change(self) -> None:
        print('放弃')

    # endregion


    # region 保存， 保存命令
    @staticmethod
    def _get_row_by_fid(model: 'EditableSqlModel', fid: int) -> Optional[int]:
        """
        根据 FId 获取 model 中对应的行号。

        :param model: 可编辑的 SQL 模型实例
        :param fid: 要查找的 FId 值
        :return: 匹配的行索引，未找到则返回 None
        """
        for row in range(model.rowCount()):
            if model.record(row).value('FId') == fid:
                return row
        return None

    def _save_table(self) -> None:
        master_table, child_table = self.table_name.get(self.ms_number)
        save_action = SavePCTableAction()

        # 修改集合中排除删除集合的fid
        self.child_update_set.difference_update(self.child_del_set)

        # 用于再定位
        master_row = self.masterTableView.currentIndex().row()
        if master_row == -1:
            DialogUtils.info('未选定左测主表的记录', '提示')
            return
        master_fid = self.masterModel.record(master_row).value('FId')

        reply, info = save_action.save_data(m_model=self.masterModel,
                                      m_table_name=master_table,
                                      m_field_list=self.m_field_list,
                                      m_type_list=self.m_type_list,
                                      m_join_list=self.m_join_list,
                                      m_update_set=self.master_update_set,
                                      m_del_set=self.master_del_set,
                                      c_model=self.childModel,
                                      c_table_name=child_table,
                                      c_field_list=self.c_field_list,
                                      c_type_list=self.c_type_list,
                                      c_join_list=self.c_join_list,
                                      c_update_set=self.child_update_set,
                                      c_del_set=self.child_del_set)
        if reply:
            self.master_update_set.clear()
            self.master_del_set.clear()
            self.child_update_set.clear()
            self.child_del_set.clear()
            self._read_master_data()
            row = self._get_row_by_fid(self.masterModel, master_fid)
            if row:
                fid = self.masterModel.record(row).value('FId')
                self._read_child_data(fid)

            self._set_save_button_enabled(False)
        else:
            DialogUtils.info('保存出错', '提示', info)

    def on_master_table_data_change(self, index) -> None:
        """响应model数据变化"""
        self._set_save_button_enabled(True)
        fid = self.masterModel.record(index.row()).value('FId')
        if fid is not None:
            self.master_update_set.add(fid)

    def on_child_table_data_change(self, index) -> None:
        """响应model数据变化"""
        self._set_save_button_enabled(True)
        fid = self.childModel.record(index.row()).value('FId')
        if fid is not None:
            self.child_update_set.add(fid)

    # endregion




if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    dic = "{'msFNumber':'05.01','msClass':'11', 'msSaveType':1, 'FPID':-1,'inspecType':0}"
    window = TwoTableEdit()
    window.show()
    sys.exit(app.exec_())
